package no.sachmuld.methodcombination.api.reflection.component;


import no.sachmuld.methodcombination.api.BehaviourChain;
import no.sachmuld.methodcombination.api.BehaviourMethod;
import no.sachmuld.methodcombination.impl.reflection.ExceptionMapper;
import no.sachmuld.methodcombination.impl.reflection.MethodNotFoundException;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 */
public abstract class DelegatingMethodCombination<B, R> implements MethodCombination<B, R> {

    private MethodCombination<B, R> executedBy;
    private List<BehaviourMethod> behaviourMethods = new ArrayList<>();
    private List<MethodCombination<B, ?>> children = new ArrayList<>();

    @Override
    public abstract DelegatingMethodCombination<B, R> build(Class behaviourClass, Method targetMethod);

    final public DelegatingMethodCombination<B, R> build(Class<? extends B> behaviourClass, Class targetClass, final String methodName, final Class<?>... parameterTypes) {
        Method targetMethod;
        try {
            targetMethod = targetClass.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            throw new MethodNotFoundException();
        }

        return build(behaviourClass, targetMethod);
    }

    final protected void executedBy(MethodCombination<B, R> executedBy) {
        this.executedBy = executedBy;
    }

    final protected void buildChildren(Class<? extends B> behaviourClass, Method targetMethod, MethodCombination<B, ?>... children) {
        this.children.addAll(Arrays.asList(children));
        for (MethodCombination<B, ?> child : children) {
            child.build(behaviourClass, targetMethod);
        }
    }

    final protected void validatedAgainst(BehaviourMethod... behaviourMethods) {
        this.behaviourMethods.addAll(Arrays.asList(behaviourMethods));
    }

    @Override
    final public void validate(BehaviourMethodValidator behaviourMethodValidator) {
        for (BehaviourMethod method : behaviourMethods) {
            behaviourMethodValidator.validate(method);
        }

        for (MethodCombination<B, ?> child : children) {
            child.validate(behaviourMethodValidator);
        }
    }

    @Override
    final public R execute(Object targetObject, BehaviourChain<? extends B> behaviourChain, Object... parameters) {
        return executedBy.execute(targetObject, behaviourChain, parameters);
    }

    final public <E extends Exception> ExceptionMapper.ExceptionMapper1<B, R, E> throwing(Class<E> e1) throws E {
        return new ExceptionMapper.ExceptionMapper1(this, e1);
    }

}
